package com.elight.utils;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

public class FileUtils {
	public final static String FILE_EXTENSION_SEPARATOR = ".";
	private static final String FOLDER_SEPARATOR = "/";
	private static final char EXTENSION_SEPARATOR = '.';

	private FileUtils() {
		throw new AssertionError();
	}

	/**
	 * 非递归遍历目录
	 * 
	 * @param file
	 *            文件目录
	 * @param resultfiles
	 *            保存返回的结果集
	 * @param endWith
	 *            为null,则返回所有文件，不为空，则返回所匹配的文件
	 */
	public static List<File> scanDir(File dir) {
		List<File> list = new ArrayList<File>();
		File file[] = dir.listFiles();
		List<File> dirList = new ArrayList<File>();
		//获得所有的目录
		for (int i = 0; i < file.length; i++) {
			if (file[i].isDirectory())
				dirList.add(file[i]);
			else {
				list.add(file[i]);
			}
		}
		//不停的扫描目录
		while (true) {
			if(dirList.size()==0){
				break;
			}
			File files[] = dirList.get(0).listFiles();
			for(File f:files){
				if(f.isDirectory()){
					dirList.add(f);
				}else{
					list.add(f);
				}
			}
			dirList.remove(0);
		}
		return list;
	}

	/**
	 * 非递归遍历目录
	 * 
	 * @param dir
	 * @param exts
	 * @return
	 */
	public static List<File> scanDir(File dir, String... exts) {
		List<File> list = new ArrayList<File>();
		File file[] = dir.listFiles();
		List<File> dirList = new ArrayList<File>();
		// 获得所有的目录
		for (int i = 0; i < file.length; i++) {
			if (file[i].isDirectory())
				dirList.add(file[i]);
			else {
				for (String ext : exts) {
					if (getFileExtension(file[i].getAbsolutePath()).toLowerCase().equals(ext.toLowerCase()))
						list.add(file[i]);
				}
			}
		}
		// 不停的扫描目录
		while (true) {
			if (dirList.size() == 0) {
				break;
			}
			File files[] = dirList.get(0).listFiles();
			for (File f : files) {
				if (f.isDirectory()) {
					dirList.add(f);
				} else {
					for (String ext : exts) {
						if (getFileExtension(f.getAbsolutePath()).toLowerCase().equals(ext.toLowerCase()))
							list.add(f);
					}
				}
			}
			dirList.remove(0);
		}
		return list;
	}
	
	/**
	 * 功能：复制文件或者文件夹。
	 * 
	 * @param inputFile
	 *            源文件
	 * @param outputFile
	 *            目的文件
	 * @param isOverWrite
	 *            是否覆盖(只针对文件)
	 * @throws IOException
	 */
	public static void copy(File inputFile, File outputFile, boolean isOverWrite) throws IOException {
		if (!inputFile.exists()) {
			throw new RuntimeException(inputFile.getPath() + "源目录不存在!");
		}
		copyPri(inputFile, outputFile, isOverWrite);
	}

	/**
	 * 功能：为copy 做递归使用。
	 * 
	 * @param inputFile
	 * @param outputFile
	 * @param isOverWrite
	 * @throws IOException
	 */
	private static void copyPri(File inputFile, File outputFile, boolean isOverWrite) throws IOException {
		// 是个文件。
		if (inputFile.isFile()) {
			copySimpleFile(inputFile, outputFile, isOverWrite);
		} else {
			// 文件夹
			if (!outputFile.exists()) {
				outputFile.mkdir();
			}
			// 循环子文件夹
			for (File child : inputFile.listFiles()) {
				copy(child, new File(outputFile.getPath() + "/" + child.getName()), isOverWrite);
			}
		}
	}

	public static void copy(File inputFile, String outputFilePath, boolean isOverWrite) throws IOException{
		File destFile=new File(outputFilePath+File.separator+inputFile.getName());
		copy(inputFile, destFile, isOverWrite);
	}
	
	
	
	
	/**
	 * 功能：copy单个文件
	 * 
	 * @param inputFile
	 *            源文件
	 * @param outputFile
	 *            目标文件
	 * @param isOverWrite
	 *            是否允许覆盖
	 * @throws IOException
	 */
	private static void copySimpleFile(File inputFile, File outputFile, boolean isOverWrite) throws IOException {
		// 目标文件已经存在
		if (outputFile.exists()) {
			if (isOverWrite) {
				if (!outputFile.delete()) {
					throw new RuntimeException(outputFile.getPath() + "无法覆盖！");
				}
			} else {
				// 不允许覆盖
				return;
			}
		}
		InputStream in = new FileInputStream(inputFile);
		OutputStream out = new FileOutputStream(outputFile);
		byte[] buffer = new byte[1024];
		int read = 0;
		while ((read = in.read(buffer)) != -1) {
			out.write(buffer, 0, read);
		}
		in.close();
		out.close();
	}

	/**
	 * 功能：删除文件
	 * 
	 * @param file
	 *            文件
	 */
	public static void delete(File file) {
		deleteFile(file);
	}

	/**
	 * 功能：删除文件，内部递归使用
	 * 
	 * @param file
	 *            文件
	 * @return boolean true 删除成功，false 删除失败。
	 */
	private static void deleteFile(File file) {
		if (file == null || !file.exists()) {
			return;
		}
		// 单文件
		if (!file.isDirectory()) {
			boolean delFlag = file.delete();
			if (!delFlag) {
				throw new RuntimeException(file.getPath() + "删除失败！");
			} else {
				return;
			}
		}
		// 删除子目录
		for (File child : file.listFiles()) {
			deleteFile(child);
		}
		// 删除自己
		file.delete();
	}

	/**
	 * 重命名文件
	 * 
	 * @param filePath
	 *            文件目录
	 * @param oldFileName
	 *            旧文件名
	 * @param newFileName
	 *            新文件名
	 */
	public static void rename(String filePath, String oldFileName, String newFileName) {
		File oldFile = new File(filePath + File.separator + oldFileName);
		File newFile = new File(filePath + File.separator + newFileName);
		moveFile(oldFile, newFile);
	}

	/**
	 * 功能：保存文件。
	 * 
	 * @param content
	 *            字节
	 * @param file
	 *            保存到的文件
	 * @throws IOException
	 */
	public static void save(byte[] content, File file) throws IOException {
		if (file == null) {
			throw new RuntimeException("保存文件不能为空");
		}
		if (content == null) {
			throw new RuntimeException("文件流不能为空");
		}
		InputStream is = new ByteArrayInputStream(content);
		save(is, file);
	}

	/**
	 * 功能：保存文件
	 * 
	 * @param streamIn
	 *            文件流
	 * @param file
	 *            保存到的文件
	 * @throws IOException
	 */
	public static void save(InputStream streamIn, File file) throws IOException {
		if (file == null) {
			throw new RuntimeException("保存文件不能为空");
		}
		if (streamIn == null) {
			throw new RuntimeException("文件流不能为空");
		}
		// 输出流
		OutputStream streamOut = null;
		// 文件夹不存在就创建。
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();
		}
		streamOut = new FileOutputStream(file);
		int bytesRead = 0;
		byte[] buffer = new byte[8192];
		while ((bytesRead = streamIn.read(buffer, 0, 8192)) != -1) {
			streamOut.write(buffer, 0, bytesRead);
		}
		streamOut.close();
		streamIn.close();
	}

	/**
	 * 读取文件返回StringBuilder
	 * 
	 * @param filePath
	 * @param charsetName
	 *            "utf-8"
	 * @return
	 */
	public static StringBuilder readFile(String filePath, String charsetName) {
		File file = new File(filePath);
		StringBuilder fileContent = new StringBuilder("");
		if (file == null || !file.isFile()) {
			return null;
		}
		BufferedReader reader = null;
		try {
			InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
			reader = new BufferedReader(is);
			String line = null;
			while ((line = reader.readLine()) != null) {
				if (!fileContent.toString().equals("")) {
					fileContent.append("\r\n");
				}
				fileContent.append(line);
			}
			reader.close();
			return fileContent;
		} catch (IOException e) {
			throw new RuntimeException("IOException occurred. ", e);
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					throw new RuntimeException("IOException occurred. ", e);
				}
			}
		}
	}

	/**
	 * 向某个文件中写入内容
	 * 
	 * @param filePath
	 *            文件路径
	 * @param content
	 *            写入的内容
	 * @param append
	 *            是否追加写入
	 * @return
	 */
	public static boolean writeFile(String filePath, String content, boolean append) {
		if (StringUtils.isEmpty(content)) {
			return false;
		}

		FileWriter fileWriter = null;
		try {
			makeDirs(filePath);
			fileWriter = new FileWriter(filePath, append);
			fileWriter.write(content);
			fileWriter.close();
			return true;
		} catch (IOException e) {
			throw new RuntimeException("IOException occurred. ", e);
		} finally {
			if (fileWriter != null) {
				try {
					fileWriter.close();
				} catch (IOException e) {
					throw new RuntimeException("IOException occurred. ", e);
				}
			}
		}
	}

	/**
	 * 将文件写入到制定目录
	 * 
	 * @param fileName
	 * @param data
	 * @param append
	 * @return
	 */
	public static boolean writeFileToSD(String fileName, String data, boolean append, String root) {
		boolean flag = false;
		FileWriter fileWriter = null;
		if (root != null) {
			String filePath = root + "/" + fileName;
			try {
				makeDirs(filePath);
				fileWriter = new FileWriter(filePath, append);
				fileWriter.write(data);
				fileWriter.write("\r\n");
				fileWriter.close();
				flag = true;
			} catch (IOException e) {
				throw new RuntimeException("IOException occurred. ", e);
			} finally {
				if (fileWriter != null) {
					try {
						fileWriter.close();
					} catch (IOException e) {
						throw new RuntimeException("IOException occurred. ", e);
					}
				}
			}
		}
		return flag;
	}

	/**
	 * 将文件Basa64得到字符串
	 * 
	 * @param fileName
	 * @return
	 */
	public static String getFileBase64Encoding(String fileName) {
		return Base64Utils.encode(getFileByte(fileName));
	}

	/**
	 * 将Base64字符串保存为文件
	 * 
	 * @param base64Str
	 */
	public static void saveFileByBase64String(String base64Str, String filePath) {
		try {
			saveFileFromByte(Base64Utils.decode(base64Str), filePath);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 根据文件得到byte
	 * 
	 * @param fileName
	 * @return
	 */
	public static byte[] getFileByte2(String fileName) {
		File file = new File(fileName);
		byte[] buffer = null;
		try {
			FileInputStream fis = new FileInputStream(file);
			ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
			byte[] b = new byte[1000];
			int n;
			while ((n = fis.read(b)) != -1) {
				bos.write(b, 0, n);
			}
			fis.close();
			bos.close();
			buffer = bos.toByteArray();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return buffer;
	}

	/**
	 * 向某个文件中写入内容
	 * 
	 * @param filePath
	 *            文件路径
	 * @param contentList
	 *            写入的内容集合 每写一个换行
	 * @param append
	 *            是否追加写入
	 * @return
	 */
	public static boolean writeFile(String filePath, List<String> contentList, boolean append) {
		if (ListUtils.isEmpty(contentList)) {
			return false;
		}

		FileWriter fileWriter = null;
		try {
			makeDirs(filePath);
			fileWriter = new FileWriter(filePath, append);
			int i = 0;
			for (String line : contentList) {
				if (i++ > 0) {
					fileWriter.write("\r\n");
				}
				fileWriter.write(line);
			}
			fileWriter.close();
			return true;
		} catch (IOException e) {
			throw new RuntimeException("IOException occurred. ", e);
		} finally {
			if (fileWriter != null) {
				try {
					fileWriter.close();
				} catch (IOException e) {
					throw new RuntimeException("IOException occurred. ", e);
				}
			}
		}
	}

	/**
	 * 向某个文件中写入内容 全新写入(不追加)
	 * 
	 * @param filePath
	 *            文件路径
	 * @param content
	 *            写入的内容
	 * @return
	 */
	public static boolean writeFile(String filePath, String content) {
		return writeFile(filePath, content, false);
	}

	/**
	 * 向某个文件中写入内容 全新写入(不追加)
	 * 
	 * @param filePath
	 *            文件路径
	 * @param contentList
	 *            写入的内容集合 每写一个换行
	 * @return
	 */
	public static boolean writeFile(String filePath, List<String> contentList) {
		return writeFile(filePath, contentList, false);
	}

	/**
	 * 向某个文件中写入内容 全新写入(不追加)
	 * 
	 * @param filePath
	 *            文件路径
	 * @param stream
	 *            输入流
	 * @return
	 */
	public static boolean writeFile(String filePath, InputStream stream) {
		return writeFile(filePath, stream, false);
	}

	/**
	 * 向某个文件中写入内容
	 * 
	 * @param filePath
	 *            文件路径
	 * @param stream
	 *            输入流
	 * @param append
	 *            是否追加写入
	 * @return
	 */
	public static boolean writeFile(String filePath, InputStream stream, boolean append) {
		return writeFile(filePath != null ? new File(filePath) : null, stream, append);
	}

	/**
	 * 向某个文件中写入内容 不追加写入
	 * 
	 * @param file
	 *            文件
	 * @param stream
	 *            输入流
	 * @return
	 */
	public static boolean writeFile(File file, InputStream stream) {
		return writeFile(file, stream, false);
	}

	/**
	 * 向某个文件中写入内容 不追加写入
	 * 
	 * @param file
	 *            文件
	 * @param stream
	 *            输入流
	 * @param append
	 *            是否追加写入
	 * @return
	 */
	public static boolean writeFile(File file, InputStream stream, boolean append) {
		OutputStream o = null;
		try {
			makeDirs(file.getAbsolutePath());
			o = new FileOutputStream(file, append);
			byte data[] = new byte[1024];
			int length = -1;
			while ((length = stream.read(data)) != -1) {
				o.write(data, 0, length);
			}
			o.flush();
			return true;
		} catch (FileNotFoundException e) {
			throw new RuntimeException("FileNotFoundException occurred. ", e);
		} catch (IOException e) {
			throw new RuntimeException("IOException occurred. ", e);
		} finally {
			if (o != null) {
				try {
					o.close();
					stream.close();
				} catch (IOException e) {
					throw new RuntimeException("IOException occurred. ", e);
				}
			}
		}
	}

	/**
	 * 移动文件位置
	 * 
	 * @param sourceFilePath
	 *            资源路径
	 * @param destFilePath
	 *            目标路径
	 */
	public static void moveFile(String sourceFilePath, String destFilePath) {
		if (StringUtils.isEmpty(sourceFilePath) || StringUtils.isEmpty(destFilePath)) {
			throw new RuntimeException("Both sourceFilePath and destFilePath cannot be null.");
		}
		moveFile(new File(sourceFilePath), new File(destFilePath));
	}

	/**
	 * move file
	 * 
	 * @param srcFile
	 * @param destFile
	 */
	public static void moveFile(File srcFile, File destFile) {
		boolean rename = srcFile.renameTo(destFile);
		if (!rename) {
			copyFile(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
			deleteFile(srcFile.getAbsolutePath());
		}
	}

	/**
	 * 复制文件
	 * 
	 * @param sourceFilePath
	 * @param destFilePath
	 * @return
	 * @throws RuntimeException
	 *             if an error occurs while operator FileOutputStream
	 */
	public static boolean copyFile(String sourceFilePath, String destFilePath) {
		InputStream inputStream = null;
		try {
			inputStream = new FileInputStream(sourceFilePath);
		} catch (FileNotFoundException e) {
			throw new RuntimeException("FileNotFoundException occurred. ", e);
		}
		return writeFile(destFilePath, inputStream);
	}

	/**
	 * 读取文件转成List 每一行为一个集合元素
	 */
	public static List<String> readFileToList(String filePath, String charsetName) {
		File file = new File(filePath);
		List<String> fileContent = new ArrayList<String>();
		if (file == null || !file.isFile()) {
			return null;
		}

		BufferedReader reader = null;
		try {
			InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
			reader = new BufferedReader(is);
			String line = null;
			while ((line = reader.readLine()) != null) {
				fileContent.add(line);
			}
			reader.close();
			return fileContent;
		} catch (IOException e) {
			throw new RuntimeException("IOException occurred. ", e);
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					throw new RuntimeException("IOException occurred. ", e);
				}
			}
		}
	}

	/**
	 * 根据路径得到文件名 可以相对路径与绝对路径 返回值：不包括扩展名
	 */
	public static String getFileNameWithoutExtension(String filePath) {
		if (StringUtils.isEmpty(filePath)) {
			return filePath;
		}

		int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
		int filePosi = filePath.lastIndexOf(File.separator);
		if (filePosi == -1) {
			return (extenPosi == -1 ? filePath : filePath.substring(0, extenPosi));
		}
		if (extenPosi == -1) {
			return filePath.substring(filePosi + 1);
		}
		return (filePosi < extenPosi ? filePath.substring(filePosi + 1, extenPosi) : filePath.substring(filePosi + 1));
	}

	/**
	 * 根据路径得到文件名 可以相对路径与绝对路径 返回值：包括扩展名
	 */
	public static String getFileName(String filePath) {
		if (StringUtils.isEmpty(filePath)) {
			return filePath;
		}

		int filePosi = filePath.lastIndexOf(File.separator);
		return (filePosi == -1) ? filePath : filePath.substring(filePosi + 1);
	}

	/**
	 * 根据文件路径得到所在的目录名
	 */
	public static String getFolderName(String filePath) {
		if (StringUtils.isEmpty(filePath)) {
			return filePath;
		}

		int filePosi = filePath.lastIndexOf(File.separator);
		return (filePosi == -1) ? "" : filePath.substring(0, filePosi);
	}

	/**
	 * 从文件路径中获得文件的拓展名
	 */
	public static String getFileExtension(String filePath) {
		if (StringUtils.isBlank(filePath)) {
			return filePath;
		}
		int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
		int filePosi = filePath.lastIndexOf(File.separator);
		if (extenPosi == -1) {
			return "";
		}
		return (filePosi >= extenPosi) ? "" : filePath.substring(extenPosi + 1);
	}

	/**
	 * 新建文件目录
	 */
	public static boolean makeDirs(String filePath) {
		String folderName = getFolderName(filePath);
		if (StringUtils.isEmpty(folderName)) {
			return false;
		}

		File folder = new File(folderName);
		return (folder.exists() && folder.isDirectory()) ? true : folder.mkdirs();
	}

	/**
	 * 新建文件目录
	 */
	public static boolean makeFolders(String filePath) {
		return makeDirs(filePath);
	}

	/**
	 * 判断文件是否存在
	 */
	public static boolean isFileExist(String filePath) {
		if (StringUtils.isBlank(filePath)) {
			return false;
		}

		File file = new File(filePath);
		return (file.exists() && file.isFile());
	}

	/**
	 * 判断目录是否存在
	 */
	public static boolean isFolderExist(String directoryPath) {
		if (StringUtils.isBlank(directoryPath)) {
			return false;
		}

		File dire = new File(directoryPath);
		return (dire.exists() && dire.isDirectory());
	}

	/**
	 * 删除文件
	 */
	public static boolean deleteFile(String path) {
		if (StringUtils.isBlank(path)) {
			return true;
		}

		File file = new File(path);
		if (!file.exists()) {
			return true;
		}
		if (file.isFile()) {
			return file.delete();
		}
		if (!file.isDirectory()) {
			return false;
		}
		for (File f : file.listFiles()) {
			if (f.isFile()) {
				f.delete();
			} else if (f.isDirectory()) {
				deleteFile(f.getAbsolutePath());
			}
		}
		return file.delete();
	}

	/**
	 * 得到文件的大小 如果文件不存在或着不是文件返回-1
	 */
	public static long getFileSize(String path) {
		if (StringUtils.isBlank(path)) {
			return -1;
		}

		File file = new File(path);
		return (file.exists() && file.isFile() ? file.length() : -1);
	}

	/**
	 * 根据路径得到文件byte
	 * 
	 * @param filePath
	 * @return
	 */
	public static byte[] getFileByte(String filePath) {
		byte[] buffer = null;
		try {
			File file = new File(filePath);
			FileInputStream fis = new FileInputStream(file);
			ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
			byte[] b = new byte[1000];
			int n;
			while ((n = fis.read(b)) != -1) {
				bos.write(b, 0, n);
			}
			fis.close();
			bos.close();
			buffer = bos.toByteArray();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return buffer;
	}

	/**
	 * 根据文件得到byte
	 * 
	 * @param file
	 * @return
	 */
	public static byte[] getFileByte(File file) {
		byte[] buffer = null;
		try {
			FileInputStream fis = new FileInputStream(file);
			ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
			byte[] b = new byte[1000];
			int n;
			while ((n = fis.read(b)) != -1) {
				bos.write(b, 0, n);
			}
			fis.close();
			bos.close();
			buffer = bos.toByteArray();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return buffer;
	}

	// 根据byte保存文件
	public static void saveFileFromByte(byte[] fileByte, String filePath) {
		BufferedOutputStream bos = null;
		FileOutputStream fos = null;
		File file = null;
		try {
			File dir = new File(filePath);
			if (!dir.exists() && dir.isDirectory()) {// 判断文件目录是否存在
				dir.mkdirs();
			}
			file = new File(filePath);
			fos = new FileOutputStream(file);
			bos = new BufferedOutputStream(fos);
			bos.write(fileByte);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (bos != null) {
				try {
					bos.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}
}
